home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
shadow-9.tar
/
shadow-9
/
shadow-960129
/
smain.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-12-17
|
14KB
|
581 lines
/*
* Copyright 1989 - 1994, John F. Haugh II
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by John F. Haugh, II
* and other contributors.
* 4. Neither the name of John F. Haugh, II nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY JOHN HAUGH AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JOHN HAUGH OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <stdio.h>
#ifndef lint
static char rcsid[] = "$Id: smain.c,v 1.3 1995/12/17 03:13:46 marekm Exp $";
#endif
/*
* Set up some BSD defines so that all the BSD ifdef's are
* kept right here
*/
#include "config.h"
#include "prototypes.h"
#include "defines.h"
#if defined(USG) || defined(SUN4) || defined(__linux__)
#include <sys/ioctl.h>
#include <termio.h>
#else
#include <sgtty.h>
#endif
#include <grp.h>
#include <signal.h>
#include "lastlog.h"
#include "pwd.h"
#ifdef SHADOWPWD
#include "shadow.h"
#endif
#include "pwauth.h"
#ifdef USE_SYSLOG
#include <syslog.h>
#ifndef __linux__
/*VARARGS*/ int syslog();
#endif
#ifndef LOG_WARN
#define LOG_WARN LOG_WARNING
#endif /* !LOG_WARN */
#endif /* USE_SYSLOG */
static char *NOT_WHEEL = "You are not authorized to su %s\n";
/*
* Password aging constants
*
* DAY - seconds in a day
* WEEK - seconds in a week
* SCALE - convert from clock to aging units
*/
#define DAY (24L*3600L)
#define WEEK (7L*DAY)
#ifdef ITI_AGING
#define SCALE (1)
#else
#define SCALE DAY
#endif
/*
* Assorted #defines to control su's behavior
*/
#ifndef MAXENV
#define MAXENV 128
#endif
/*
* Global variables
*/
char hush[BUFSIZ];
char name[BUFSIZ];
char pass[BUFSIZ];
char home[BUFSIZ];
char prog[BUFSIZ];
char mail[BUFSIZ];
char oldname[BUFSIZ];
char *newenvp[MAXENV];
char *Prog;
int newenvc = 0;
int maxenv = MAXENV;
struct passwd pwent;
/*
* External identifiers
*/
extern void addenv ();
extern void entry ();
extern void sulog ();
extern void subsystem ();
extern void setup ();
extern void motd ();
extern void mailcheck ();
extern void shell ();
extern char *ttyname ();
extern char *getenv ();
extern char *getpass ();
extern char *tz ();
extern char *pw_encrypt();
extern int pw_auth();
#ifndef __STDC__
extern struct passwd *getpwuid ();
extern struct passwd *getpwnam ();
#ifdef SHADOWPWD
extern struct spwd *getspnam ();
#endif
#endif /* !__STDC__ */
extern char *getdef_str();
extern int getdef_bool();
extern char **environ;
/*
* die - set or reset termio modes.
*
* die() is called before processing begins. signal() is then
* called with die() as the signal handler. If signal later
* calls die() with a signal number, the terminal modes are
* then reset.
*/
SIGTYPE
#if defined(__STDC__)
die (int killed)
#else
die (killed)
int killed;
#endif
{
#if defined(BSD) || defined(SUN)
static struct sgttyb sgtty;
if (killed)
stty (0, &sgtty);
else
gtty (0, &sgtty);
#else
static struct termio sgtty;
if (killed)
ioctl (0, TCSETA, &sgtty);
else
ioctl (0, TCGETA, &sgtty);
#endif
if (killed) {
#ifdef USE_SYSLOG
closelog ();
#endif
exit (killed);
}
}
int
iswheel(name)
char *name;
{
struct group *grp;
char **p;
grp = getgrgid(0);
if (!grp || !grp->gr_mem)
return 0;
for (p = &grp->gr_mem[0]; *p; p++)
if (!strcmp(name, *p))
return 1;
return 0;
}
/*
* su - switch user id
*
* su changes the user's ids to the values for the specified user.
* if no new user name is specified, "root" is used by default.
*
* The only valid option is a "-" character, which is interpreted
* as requiring a new login session to be simulated.
*
* Any additional arguments are passed to the user's shell. In
* particular, the argument "-c" will cause the next argument to
* be interpreted as a command by the common shell programs.
*/
int main (argc, argv, envp)
int argc;
char **argv;
char **envp;
{
SIGTYPE (*oldsig)();
char *cp;
char arg0[64];
char *tty = 0; /* Name of tty SU is run from */
int doshell = 0;
int fakelogin = 0;
int amroot = 0;
int my_uid;
struct passwd *pw = 0;
#ifdef SHADOWPWD
struct spwd *spwd = 0;
#endif
/*
* Get the program name. The program name is used as a
* prefix to most error messages. It is also used as input
* to the openlog() function for error logging.
*/
Prog = basename(argv[0]);
#ifdef USE_SYSLOG
openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
#endif
/*
* Get the tty name. Entries will be logged indicating that
* the user tried to change to the named new user from the
* current terminal.
*/
if (isatty (0) && (cp = ttyname (0))) {
if (strncmp (cp, "/dev/", 5) == 0)
tty = cp + 5;
else
tty = cp;
} else
tty = "???";
/*
* Process the command line arguments.
*/
argc--; argv++; /* shift out command name */
if (argc > 0 && argv[0][0] == '-' && argv[0][1] == '\0') {
fakelogin = 1;
argc--; argv++; /* shift ... */
}
/*
* If a new login is being set up, the old environment will
* be ignored and a new one created later on.
*/
if (! fakelogin)
while (*envp)
addenv (*envp++);
if (fakelogin && (cp=getdef_str("ENV_TZ")))
addenv (*cp == '/' ? tz(cp) : cp);
/*
* The clock frequency will be reset to the login value if required
*/
if (fakelogin && (cp=getdef_str("ENV_HZ")) )
addenv (cp); /* set the default $HZ, if one */
/*
* The terminal type will be left alone if it is present in the
* environment already.
*/
if (fakelogin && (cp = getenv ("TERM"))) {
char term[BUFSIZ];
if (strlen(cp) + 5 < sizeof term) {
strcpy(term, "TERM=");
strcat(term, cp);
addenv(term);
}
}
/*
* The next argument must be either a user ID, or some flag to
* a subshell. Pretty sticky since you can't have an argument
* which doesn't start with a "-" unless you specify the new user
* name. Any remaining arguments will be passed to the user's
* login shell.
*/
if (argc > 0 && argv[0][0] != '-') {
STRFCPY(name, argv[0]); /* use this login id */
argc--; argv++; /* shift ... */
}
if (! name[0]) /* use default user ID */
(void) strcpy (name, "root");
doshell = argc == 0; /* any arguments remaining? */
/*
* Get the user's real name. The current UID is used to determine
* who has executed su. That user ID must exist.
*/
/* Changed to try getlogin() first and use the old method only if
getlogin() fails. Should handle shared uid better. --marekm */
my_uid = getuid();
amroot = (my_uid == 0);
cp = getlogin();
pw = cp ? getpwnam(cp) : getpwuid(my_uid);
if (!pw) {
#ifdef USE_SYSLOG
syslog (LOG_CRIT, "Unknown UID: %d (%s)\n",
my_uid, cp ? cp : "???");
#endif
goto failure;
}
STRFCPY(oldname, pw->pw_name);
top:
/*
* This is the common point for validating a user whose name
* is known. It will be reached either by normal processing,
* or if the user is to be logged into a subsystem root.
*
* The password file entries for the user is gotten and the
* account validated.
*/
if ((pw = getpwnam (name))) {
#ifdef SHADOWPWD
if ((spwd = getspnam (name)))
pw->pw_passwd = spwd->sp_pwdp;
#else
;
#endif
} else {
(void) fprintf (stderr, "Unknown id: %s\n", name);
#ifdef USE_SYSLOG
closelog ();
#endif
exit (1);
}
pwent = *pw;
/*
* BSD systems only allow "wheel" to SU to root. USG systems
* don't, so we make this a configurable option.
*/
/* The original Shadow 3.3.2 did this differently. Do it like BSD:
- check for uid 0 instead of name "root" - there are systems
with several root accounts under different names,
- check the contents of /etc/group instead of the current group
set (if the group is not empty, you must be listed as a member,
primary group id 0 is not sufficient).
TODO:
FreeBSD su can be compiled to ask the current user's password
instead of the root password if the user is a member of the
wheel group. This might be good for large systems with more
than one administrator, so that they don't have to share the
same root password and remember two passwords... --marekm */
if (!amroot && pwent.pw_uid == 0 && getdef_bool("SU_WHEEL_ONLY")
&& !iswheel(oldname)) {
fprintf(stderr, NOT_WHEEL, name);
exit(1);
}
/*
* Set the default shell.
*/
if (pwent.pw_shell[0] == '\0')
pwent.pw_shell = "/bin/sh";
/*
* Set up a signal handler in case the user types QUIT.
*/
die (0);
oldsig = signal (SIGQUIT, die);
/*
* See if the system defined authentication method is being used.
* The first character of an administrator defined method is an
* '@' character.
*/
if (! amroot && pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0)) {
#ifdef USE_SYSLOG
syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
"Authentication failed for %s\n", name);
#endif
failure:
sulog (tty, 0); /* log failed attempt */
puts ("Sorry.");
#ifdef USE_SYSLOG
if ( getdef_bool("SYSLOG_SU_ENAB") )
syslog (pwent.pw_uid ? LOG_INFO:LOG_CRIT,
"- %s %s-%s\n", tty,
oldname[0] ? oldname:"???",
name[0] ? name:"???");
closelog ();
#endif
exit (1);
}
(void) signal (SIGQUIT, oldsig);
/*
* Check to see if the account is expired. root gets to
* ignore any expired accounts, but normal users can't become
* a user with an expired password.
*/
if (! amroot) {
#ifdef SHADOWPWD
if (spwd) {
if (isexpired (&pwent, spwd)) {
#ifdef USE_SYSLOG
syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
"Expired account %s\n", name);
#endif
goto failure;
}
}
#else
if (0)
; /* There is no password to be expired */
#endif
#if defined(ATT_AGE) && defined(AGING)
else if (pwent.pw_age[0] &&
isexpired (&pwent)) {
#ifdef USE_SYSLOG
syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
"Expired account %s\n", name);
#endif
goto failure;
}
#endif /* ATT_AGE */
}
/*
* Check to see if the account permits "su". root gets to
* ignore any restricted accounts, but normal users can't become
* a user if there is a "SU" entry in the /etc/porttime file
* denying access to the account.
*/
if (! amroot) {
if (! isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
#ifdef USE_SYSLOG
syslog (pwent.pw_uid ? LOG_WARN:LOG_CRIT,
"SU by %s to restricted account %s\n",
oldname, name);
#endif
goto failure;
}
}
cp = getdef_str( pwent.pw_uid == 0 ? "ENV_SUPATH" : "ENV_PATH" );
addenv( cp != NULL ? cp : "PATH=/bin:/usr/bin" );
environ = newenvp; /* make new environment active */
if (getenv ("IFS")) /* don't export user IFS ... */
addenv ("IFS= \t\n"); /* ... instead, set a safe IFS */
if (pwent.pw_shell[0] == '*') { /* subsystem root required */
subsystem (&pwent); /* figure out what to execute */
endpwent ();
#ifdef SHADOWPWD
endspent ();
#endif
goto top;
}
sulog (tty, 1); /* save SU information */
endpwent ();
#ifdef SHADOWPWD
endspent ();
#endif
#ifdef USE_SYSLOG
if ( getdef_bool("SYSLOG_SU_ENAB") )
syslog (LOG_INFO, "+ %s %s-%s\n", tty,
oldname[0] ? oldname:"???", name[0] ? name:"???");
#endif
if (fakelogin)
setup (&pwent); /* set UID, GID, HOME, etc ... */
else {
if (setgid (pwent.pw_gid) || setuid (pwent.pw_uid)) {
perror ("Can't set ID");
#ifdef USE_SYSLOG
syslog (LOG_CRIT, "Unable to set uid = %d, gid = %d\n",
pwent.pw_uid, pwent.pw_gid);
closelog ();
#endif
exit (1);
}
}
/*
* See if the user has extra arguments on the command line. In
* that case they will be provided to the new user's shell as
* arguments.
*/
if (! doshell) {
/*
* Use new user's shell from /etc/passwd and create an
* argv with the rest of the command line included.
*/
argv[-1] = pwent.pw_shell;
(void) execv (pwent.pw_shell, &argv[-1]);
(void) fprintf (stderr, "No shell\n");
#ifdef USE_SYSLOG
syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
closelog ();
#endif
exit (1);
}
if (fakelogin) {
if (! hushed (&pwent)) {
motd ();
mailcheck ();
}
cp = getdef_str("SU_NAME");
if (!cp)
cp = basename(pwent.pw_shell);
arg0[0] = '-';
strncpy(arg0+1, cp, sizeof(arg0)-2);
arg0[sizeof(arg0)-1] = '\0';
cp = arg0;
} else
cp = basename(pwent.pw_shell);
shell (pwent.pw_shell, cp);
#if 0 /* This doesn't work anyway, shell() never returns... --marekm */
#ifdef USE_SYSLOG
syslog (LOG_WARN, "Cannot execute %s\n", pwent.pw_shell);
closelog ();
#endif
exit (1);
#endif
/*NOTREACHED*/
return 1;
}